<!doctype html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=robots content="noodp"><meta http-equiv=x-ua-compatible content="IE=edge, chrome=1"><title>Java高并发秒杀API(二)之Service层 - Yulin Lewis' Blog</title><meta name=keywords content="雨临Lewis,Java,hugo,hexo,博客"><meta name=Description content="不想当写手的码农不是好咸鱼_(xз」∠)_"><meta property="og:title" content="Java高并发秒杀API(二)之Service层"><meta property="og:description" content="1. 设计前的分析

分层的必要性


DAO层工作演变为:接口设计+SQL编写（不需要其他杂七杂八的功能）
代码和SQL的分离,方便review（浏览）
DAO拼接等逻辑在Service层完成（DAO只需负责SQL语句，其他都由Service层完成）"><meta property="og:type" content="article"><meta property="og:url" content="https://lewky.cn/posts/18357.html/"><meta property="og:image" content="https://lewky.cn/logo.png"><meta property="article:published_time" content="2017-10-05T18:02:28+08:00"><meta property="article:modified_time" content="2017-10-05T18:02:28+08:00"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://lewky.cn/logo.png"><meta name=twitter:title content="Java高并发秒杀API(二)之Service层"><meta name=twitter:description content="1. 设计前的分析

分层的必要性


DAO层工作演变为:接口设计+SQL编写（不需要其他杂七杂八的功能）
代码和SQL的分离,方便review（浏览）
DAO拼接等逻辑在Service层完成（DAO只需负责SQL语句，其他都由Service层完成）"><meta name=application-name content="雨临Lewis的博客"><meta name=apple-mobile-web-app-title content="雨临Lewis的博客"><meta name=theme-color content="#ffffff"><meta name=msapplication-TileColor content="#da532c"><link rel="shortcut icon" type=image/x-icon href=/favicon.ico><link rel=icon type=image/png sizes=32x32 href=/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon-16x16.png><link rel=apple-touch-icon sizes=180x180 href=/apple-touch-icon.png><link rel=mask-icon href=/safari-pinned-tab.svg color=#5bbad5><link rel=manifest href=/site.webmanifest><link rel=canonical href=https://lewky.cn/posts/18357.html/><link rel=prev href=https://lewky.cn/posts/56225.html/><link rel=next href=https://lewky.cn/posts/33818.html/><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css><link rel=stylesheet href=/css/style.min.css><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/animate.css@3.7.2/animate.min.css><script type=application/ld+json>{"@context":"http://schema.org","@type":"BlogPosting","headline":"Java高并发秒杀API(二)之Service层","inLanguage":"","mainEntityOfPage":{"@type":"WebPage","@id":"https:\/\/lewky.cn\/posts\/18357.html\/"},"genre":"posts","keywords":"项目笔记, SSM实战项目, 秒杀","wordcount":7260,"url":"https:\/\/lewky.cn\/posts\/18357.html\/","datePublished":"2017-10-05T18:02:28+08:00","dateModified":"2017-10-05T18:02:28+08:00","license":"This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.","publisher":{"@type":"Organization","name":"雨临Lewis","logo":"https:\/\/lewky.cn\/images\/avatar.jpg"},"author":{"@type":"Person","name":"雨临Lewis"},"description":""}</script></head><body header-desktop=fixed header-mobile=auto><script type=text/javascript>(window.localStorage&&localStorage.getItem('theme')?localStorage.getItem('theme')==='dark':('auto'==='auto'?window.matchMedia('(prefers-color-scheme: dark)').matches:'auto'==='dark'))&&document.body.setAttribute('theme','dark');</script><div id=mask></div><div class=wrapper><header class=desktop id=header-desktop><div class=header-wrapper><a href=https://github.com/lewky class=github-corner target=_blank title="Follow me on GitHub" aria-label="Follow me on GitHub"><svg width="3.5rem" height="3.5rem" viewBox="0 0 250 250" style="fill:#70b7fd;color:#fff;position:absolute;top:0;border:0;left:0;transform:scale(-1,1)" aria-hidden="true"><path d="M0 0 115 115h15l12 27L250 250V0z"/><path d="M128.3 109C113.8 99.7 119 89.6 119 89.6 122 82.7 120.5 78.6 120.5 78.6 119.2 72 123.4 76.3 123.4 76.3 127.3 80.9 125.5 87.3 125.5 87.3 122.9 97.6 130.6 101.9 134.4 103.2" fill="currentcolor" style="transform-origin:130px 106px" class="octo-arm"/><path d="M115 115C114.9 115.1 118.7 116.5 119.8 115.4L133.7 101.6C136.9 99.2 139.9 98.4 142.2 98.6 133.8 88 127.5 74.4 143.8 58 148.5 53.4 154 51.2 159.7 51 160.3 49.4 163.2 43.6 171.4 40.1 171.4 40.1 176.1 42.5 178.8 56.2 183.1 58.6 187.2 61.8 190.9 65.4 194.5 69 197.7 73.2 200.1 77.6 213.8 80.2 216.3 84.9 216.3 84.9 212.7 93.1 206.9 96 205.4 96.6 205.1 102.4 203 107.8 198.3 112.5 181.9 128.9 168.3 122.5 157.7 114.1 157.9 116.9 156.7 120.9 152.7 124.9L141 136.5C139.8 137.7 141.6 141.9 141.8 141.8z" fill="currentcolor" class="octo-body"/></svg></a><div class=header-title><a href=/ title="Yulin Lewis' Blog"><span class=header-title-pre><i class="fas fa-fw fa-atom"></i></span>雨临Lewis</a></div><div class=menu><div class=menu-inner><div class=dropdown><a href=/posts/ class="menu-item menu-more dropbtn" title=点击查看所有文章><i class="fas fa-fw fa-archive"></i>归档</a><div class="menu-more-content dropdown-content"><a href=/categories/><i class="fas fa-fw fa-th"></i>分类 </a><a href=/tags/><i class="fas fa-fw fa-tag"></i>标签 </a><a href=/hot/ title=文章热度Top15><i class="fas fa-fw fa-fire"></i>热度 </a><a href=/donation/ title=感谢打赏，老板大气~><i class="fas fa-fw fa-donate"></i>打赏 </a><a href=/about/><i class="fas fa-fw fa-at"></i>关于</a></div></div><a class=menu-item href=/friends/ title=欢迎申请友链><i class="fas fa-fw fa-link"></i>友链</a><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn" title=一些有意思的东东~><i class="fas fa-fw fa-fan fa-spin"></i>趣味</a><div class="menu-more-content dropdown-content"><a href=/funny/high/ title="前方高能♂ 小心外放！" target=_blank rel=noopener><i class="fas fa-fw fa-dizzy"></i>燥起来！ </a><a href=/funny/mikutap/ title=初音未来音乐游戏 target=_blank rel=noopener><i class="fas fa-fw fa-music"></i>在线打碟 </a><a href=/funny/catch-the-cat/ title=逮住那只猫!><i class="fas fa-fw fa-cat"></i>抓住猫咪</a></div></div><div class=dropdown><a href=/ class="menu-item menu-more dropbtn" title=回到首页><i class="fas fa-fw fa-atom"></i>站点</a><div class="menu-more-content dropdown-content"><a href=/bbs/ title=来留言吧~><i class="fas fa-fw fa-comment"></i>公告留言 </a><a href=https://seo.chinaz.com target=_blank rel=noopener><i class="fas fa-fw fa-chart-line"></i>SEO查询 </a><a href=https://www.ping.cn/http/lewky.cn target=_blank rel=noopener><i class="fas fa-fw fa-bezier-curve"></i>网站测速 </a><a href=/posts/e62c38c4.html/><i class="fas fa-fw fa-cog fa-spin"></i>建站日志</a></div></div><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn"><i class="fas fa-fw fa-book"></i>文档</a><div class="menu-more-content dropdown-content"><a href=https://gohugo.io/documentation/ target=_blank rel=noopener><i class="fas fa-fw fa-star"></i>Hugo文档 </a><a href=https://javanote.doc.lewky.cn/ title=尚在完善中~ target=_blank rel=noopener><i class="fab fa-fw fa-java"></i>Java 笔记</a></div></div><a class=menu-item href=/search/ title=渲染搜索结果需要等待几秒钟~><i class="fas fa-fw fa-search"></i>搜索
</a><span class="menu-item delimiter"></span><a href=https://travellings.link target=_blank class=menu-item rel=noopener title=开往-友链接力><i class="fas fa-fw fa-subway"></i></a><a href=https://rssblog.vercel.app/ class=menu-item target=_blank rel=noopener title=RSSBlog><i class="fas fa-fw fa-inbox"></i></a><a href=https://github.com/lewky class=menu-item target=_blank rel=noopener title=GitHub><i class="fab fa-fw fa-github"></i></a></div></div></div></header><header class=mobile id=header-mobile><div class=header-container><div class=header-wrapper><div class=header-title><a href=/ title="Yulin Lewis' Blog"><span class=header-title-pre><i class="fas fa-fw fa-atom"></i></span>雨临Lewis</a></div><div class=menu-toggle id=menu-toggle-mobile><span></span><span></span><span></span></div></div><div class=menu id=menu-mobile><div class=dropdown><a href=/posts/ class="menu-item menu-more dropbtn" title=点击查看所有文章><i class="fas fa-fw fa-archive"></i>归档</a><div class="menu-more-content dropdown-content"><a href=/categories/><i class="fas fa-fw fa-th"></i>分类 </a><a href=/tags/><i class="fas fa-fw fa-tag"></i>标签 </a><a href=/hot/ title=文章热度Top15><i class="fas fa-fw fa-fire"></i>热度 </a><a href=/donation/ title=感谢打赏，老板大气~><i class="fas fa-fw fa-donate"></i>打赏 </a><a href=/about/><i class="fas fa-fw fa-at"></i>关于</a></div></div><a class=menu-item href=/friends/ title=欢迎申请友链><i class="fas fa-fw fa-link"></i>友链</a><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn" title=一些有意思的东东~><i class="fas fa-fw fa-fan fa-spin"></i>趣味</a><div class="menu-more-content dropdown-content"><a href=/funny/high/ title="前方高能♂ 小心外放！" target=_blank rel=noopener><i class="fas fa-fw fa-dizzy"></i>燥起来！ </a><a href=/funny/mikutap/ title=初音未来音乐游戏 target=_blank rel=noopener><i class="fas fa-fw fa-music"></i>在线打碟 </a><a href=/funny/catch-the-cat/ title=逮住那只猫!><i class="fas fa-fw fa-cat"></i>抓住猫咪</a></div></div><div class=dropdown><a href=/ class="menu-item menu-more dropbtn" title=回到首页><i class="fas fa-fw fa-atom"></i>站点</a><div class="menu-more-content dropdown-content"><a href=/bbs/ title=来留言吧~><i class="fas fa-fw fa-comment"></i>公告留言 </a><a href=https://seo.chinaz.com target=_blank rel=noopener><i class="fas fa-fw fa-chart-line"></i>SEO查询 </a><a href=https://www.ping.cn/http/lewky.cn target=_blank rel=noopener><i class="fas fa-fw fa-bezier-curve"></i>网站测速 </a><a href=/posts/e62c38c4.html/><i class="fas fa-fw fa-cog fa-spin"></i>建站日志</a></div></div><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn"><i class="fas fa-fw fa-book"></i>文档</a><div class="menu-more-content dropdown-content"><a href=https://gohugo.io/documentation/ target=_blank rel=noopener><i class="fas fa-fw fa-star"></i>Hugo文档 </a><a href=https://javanote.doc.lewky.cn/ title=尚在完善中~ target=_blank rel=noopener><i class="fab fa-fw fa-java"></i>Java 笔记</a></div></div><a class=menu-item href=/search/ title=渲染搜索结果需要等待几秒钟~><i class="fas fa-fw fa-search"></i>搜索</a>
<a href=https://travellings.link target=_blank class=menu-item rel=noopener title=开往-友链接力><i class="fas fa-fw fa-subway"></i></a><a href=https://rssblog.vercel.app/ class=menu-item target=_blank rel=noopener title=RSSBlog><i class="fas fa-fw fa-inbox"></i></a><a href=https://github.com/lewky class=menu-item target=_blank rel=noopener title=GitHub><i class="fab fa-fw fa-github"></i></a></div></div></header><div class="search-dropdown desktop"><div id=search-dropdown-desktop></div></div><div class="search-dropdown mobile"><div id=search-dropdown-mobile></div></div><main class=main><div class=container><article class="page single"><h1 class="single-title animated flipInX">Java高并发秒杀API(二)之Service层</h1><div class=post-meta><div class=post-meta-line><span class=post-author><a href=/ title=Author rel=author class=author><i class="fas fa-user-circle fa-fw"></i>雨临Lewis</a></span>&nbsp;<span class=post-category>收录于 <a href=/categories/seckill/><i class="far fa-folder fa-fw"></i>seckill</a></span></div><div class=post-meta-line><i class="far fa-calendar fa-fw"></i>&nbsp;<time datetime=2017-10-05>2017-10-05</time>&nbsp;<i class="far fa-calendar-plus fa-fw"></i>&nbsp;<time datetime=2017-10-05>2017-10-05</time>&nbsp;<i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 7260 字
<i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 15 分钟&nbsp;<span id=/posts/18357.html/ class=leancloud_visitors data-flag-title=Java高并发秒杀API(二)之Service层>
<i class="far fa-eye fa-fw"></i>&nbsp;<span class=leancloud-visitors-count></span>&nbsp;次阅读
</span>&nbsp;
<a href=#comments id=post-meta-vcount title=查看评论><i class="fas fa-comment fa-fw"></i>&nbsp;<span id=/posts/18357.html/ class=waline-comment-count></span>&nbsp;条评论</a></div></div><div class=content id=content><h2 id=1-设计前的分析><strong>1. 设计前的分析</strong></h2><blockquote><p><strong>分层的必要性</strong></p></blockquote><ul><li>DAO层工作演变为:接口设计+SQL编写（不需要其他杂七杂八的功能）</li><li>代码和SQL的分离,方便review（浏览）</li><li>DAO拼接等逻辑在Service层完成（DAO只需负责SQL语句，其他都由Service层完成）</li></ul><p>一些初学者容易出现的错误，就是喜欢在DAO层进行逻辑的编写，其实DAO就是数据访问的缩写，它只进行数据的访问操作。</p><blockquote><p><strong>业务接口的编写</strong></p></blockquote><p>初学者总是关注细节，关注接口如何去实现，这样设计出来的接口往往比较冗余。业务接口的编写要站在“使用者”的角度定义，三个方面：方法定义的粒度、参数、返回值。</p><ul><li>方法定义粒度：关注接口的功能本身，至于这个功能需要包含哪些步骤那是具体的实现，也就是说，功能明确而且单一。</li><li>参数：方法所需要的数据，供使用者传入，明确方法所需要的数据，而且尽可能友好，简练。</li><li>返回值：一般情况下，entity数据不够，需要自定义DTO,也有可能抛出异常，需要自定义异常，不管是DTO还是异常，尽可能将接口调用的信息返回给使用者，哪怕是失败信息。</li></ul><blockquote><p><strong>DTO与entity的区别</strong></p></blockquote><p>DTO数据传输层：用于Web层和Service层之间传递的数据封装。</p><p>entity：用于业务数据的封装，比如数据库中的数据。</p><blockquote><p><strong>关于秒杀地址的暴露</strong></p></blockquote><ol><li>需要有专门一个方法实现秒杀地址输出，避免人为因素提前知道秒杀地址而出现漏洞。</li><li>获取秒杀url时，如果不合法，则返回当前时间和秒杀项目的时间；如果合法，才返回md5加密后url，以避免url被提前获知。</li><li>使用md5将url加密、校验，防止秒杀的url被篡改。</li></ol><blockquote><p><strong>MD5加密</strong></p></blockquote><p>Spring提供了MD5生成工具。代码如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=n>DigestUtils</span><span class=o>.</span><span class=na>md5DigestAsHex</span><span class=o>();</span>
</code></pre></td></tr></table></div></div><p>MD5盐值字符串（salt），用于混淆MD5，添加MD5反编译难度</p><h2 id=2-service层的接口设计><strong>2. Service层的接口设计</strong></h2><p>在<code>src/main/java</code>包下建立<code>com.lewis.service</code>包，用来存放Service接口；在<code>src/main/java</code>包下建立<code>com.lewis.exception</code>包，用来存放Service层出现的异常类：比如重复秒杀异常、秒杀已关闭异常；在<code>src/main/java</code>包下建立<code>com.lewis.dto</code>包，用来封装Web层和Service层之间传递的数据。</p><blockquote><p><strong>定义SeckillService接口</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span><span class=lnt>27
</span><span class=lnt>28
</span><span class=lnt>29
</span><span class=lnt>30
</span><span class=lnt>31
</span><span class=lnt>32
</span><span class=lnt>33
</span><span class=lnt>34
</span><span class=lnt>35
</span><span class=lnt>36
</span><span class=lnt>37
</span><span class=lnt>38
</span><span class=lnt>39
</span><span class=lnt>40
</span><span class=lnt>41
</span><span class=lnt>42
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=cm>/**
</span><span class=cm>	 * 业务接口:站在使用者(程序员)的角度设计接口 三个方面:1.方法定义粒度，方法定义的要非常清楚2.参数，要越简练越好 3.返回类型(return
</span><span class=cm>	 * 类型一定要友好/或者return异常，我们允许的异常)
</span><span class=cm>	 */</span>
	<span class=kd>public</span> <span class=kd>interface</span> <span class=nc>SeckillService</span> <span class=o>{</span>
	
		<span class=cm>/**
</span><span class=cm>		 * 查询全部的秒杀记录
</span><span class=cm>		 * 
</span><span class=cm>		 * @return
</span><span class=cm>		 */</span>
		<span class=n>List</span><span class=o>&lt;</span><span class=n>Seckill</span><span class=o>&gt;</span> <span class=nf>getSeckillList</span><span class=o>();</span>
	
		<span class=cm>/**
</span><span class=cm>		 * 查询单个秒杀记录
</span><span class=cm>		 * 
</span><span class=cm>		 * @param seckillId
</span><span class=cm>		 * @return
</span><span class=cm>		 */</span>
		<span class=n>Seckill</span> <span class=nf>getById</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>);</span>
	
		<span class=c1>// 再往下，是我们最重要的行为的一些接口
</span><span class=c1></span>	
		<span class=cm>/**
</span><span class=cm>		 * 在秒杀开启时输出秒杀接口的地址，否则输出系统时间和秒杀时间
</span><span class=cm>		 * 
</span><span class=cm>		 * @param seckillId 秒杀商品Id
</span><span class=cm>		 * @return 根据对应的状态返回对应的状态实体
</span><span class=cm>		 */</span>
		<span class=n>Exposer</span> <span class=nf>exportSeckillUrl</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>);</span>
	
		<span class=cm>/**
</span><span class=cm>		 * 执行秒杀操作，有可能失败，有可能成功，所以要抛出我们允许的异常
</span><span class=cm>		 * 
</span><span class=cm>		 * @param seckillId 秒杀的商品ID
</span><span class=cm>		 * @param userPhone 手机号码
</span><span class=cm>		 * @param md5 md5加密值
</span><span class=cm>		 * @return 根据不同的结果返回不同的实体信息
</span><span class=cm>		 */</span>
		<span class=n>SeckillExecution</span> <span class=nf>executeSeckill</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=kt>long</span> <span class=n>userPhone</span><span class=o>,</span> <span class=n>String</span> <span class=n>md5</span><span class=o>)</span> <span class=kd>throws</span> <span class=n>SeckillException</span><span class=o>,</span>
				<span class=n>RepeatKillException</span><span class=o>,</span> <span class=n>SeckillCloseException</span><span class=o>;</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>在dto包中创建Exposer.java，用于封装秒杀的地址信息</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span><span class=lnt>27
</span><span class=lnt>28
</span><span class=lnt>29
</span><span class=lnt>30
</span><span class=lnt>31
</span><span class=lnt>32
</span><span class=lnt>33
</span><span class=lnt>34
</span><span class=lnt>35
</span><span class=lnt>36
</span><span class=lnt>37
</span><span class=lnt>38
</span><span class=lnt>39
</span><span class=lnt>40
</span><span class=lnt>41
</span><span class=lnt>42
</span><span class=lnt>43
</span><span class=lnt>44
</span><span class=lnt>45
</span><span class=lnt>46
</span><span class=lnt>47
</span><span class=lnt>48
</span><span class=lnt>49
</span><span class=lnt>50
</span><span class=lnt>51
</span><span class=lnt>52
</span><span class=lnt>53
</span><span class=lnt>54
</span><span class=lnt>55
</span><span class=lnt>56
</span><span class=lnt>57
</span><span class=lnt>58
</span><span class=lnt>59
</span><span class=lnt>60
</span><span class=lnt>61
</span><span class=lnt>62
</span><span class=lnt>63
</span><span class=lnt>64
</span><span class=lnt>65
</span><span class=lnt>66
</span><span class=lnt>67
</span><span class=lnt>68
</span><span class=lnt>69
</span><span class=lnt>70
</span><span class=lnt>71
</span><span class=lnt>72
</span><span class=lnt>73
</span><span class=lnt>74
</span><span class=lnt>75
</span><span class=lnt>76
</span><span class=lnt>77
</span><span class=lnt>78
</span><span class=lnt>79
</span><span class=lnt>80
</span><span class=lnt>81
</span><span class=lnt>82
</span><span class=lnt>83
</span><span class=lnt>84
</span><span class=lnt>85
</span><span class=lnt>86
</span><span class=lnt>87
</span><span class=lnt>88
</span><span class=lnt>89
</span><span class=lnt>90
</span><span class=lnt>91
</span><span class=lnt>92
</span><span class=lnt>93
</span><span class=lnt>94
</span><span class=lnt>95
</span><span class=lnt>96
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=cm>/**
</span><span class=cm>	 * 暴露秒杀地址(接口)DTO
</span><span class=cm>	 */</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>Exposer</span> <span class=o>{</span>
	
		<span class=c1>// 是否开启秒杀
</span><span class=c1></span>		<span class=kd>private</span> <span class=kt>boolean</span> <span class=n>exposed</span><span class=o>;</span>
	
		<span class=c1>// 加密措施
</span><span class=c1></span>		<span class=kd>private</span> <span class=n>String</span> <span class=n>md5</span><span class=o>;</span>
	
		<span class=c1>//id为seckillId的商品的秒杀地址
</span><span class=c1></span>		<span class=kd>private</span> <span class=kt>long</span> <span class=n>seckillId</span><span class=o>;</span>
	
		<span class=c1>// 系统当前时间(毫秒)
</span><span class=c1></span>		<span class=kd>private</span> <span class=kt>long</span> <span class=n>now</span><span class=o>;</span>
	
		<span class=c1>// 秒杀的开启时间
</span><span class=c1></span>		<span class=kd>private</span> <span class=kt>long</span> <span class=n>start</span><span class=o>;</span>
	
		<span class=c1>// 秒杀的结束时间
</span><span class=c1></span>		<span class=kd>private</span> <span class=kt>long</span> <span class=n>end</span><span class=o>;</span>
	
		<span class=kd>public</span> <span class=nf>Exposer</span><span class=o>(</span><span class=kt>boolean</span> <span class=n>exposed</span><span class=o>,</span> <span class=n>String</span> <span class=n>md5</span><span class=o>,</span> <span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>exposed</span> <span class=o>=</span> <span class=n>exposed</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>md5</span> <span class=o>=</span> <span class=n>md5</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=nf>Exposer</span><span class=o>(</span><span class=kt>boolean</span> <span class=n>exposed</span><span class=o>,</span> <span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=kt>long</span> <span class=n>now</span><span class=o>,</span> <span class=kt>long</span> <span class=n>start</span><span class=o>,</span> <span class=kt>long</span> <span class=n>end</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>exposed</span> <span class=o>=</span> <span class=n>exposed</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>now</span> <span class=o>=</span> <span class=n>now</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>start</span> <span class=o>=</span> <span class=n>start</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>end</span> <span class=o>=</span> <span class=n>end</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=nf>Exposer</span><span class=o>(</span><span class=kt>boolean</span> <span class=n>exposed</span><span class=o>,</span> <span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>exposed</span> <span class=o>=</span> <span class=n>exposed</span><span class=o>;</span>
			<span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>boolean</span> <span class=nf>isExposed</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=n>exposed</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>void</span> <span class=nf>setExposed</span><span class=o>(</span><span class=kt>boolean</span> <span class=n>exposed</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>exposed</span> <span class=o>=</span> <span class=n>exposed</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=n>String</span> <span class=nf>getMd5</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=n>md5</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>void</span> <span class=nf>setMd5</span><span class=o>(</span><span class=n>String</span> <span class=n>md5</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>md5</span> <span class=o>=</span> <span class=n>md5</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>long</span> <span class=nf>getSeckillId</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=n>seckillId</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>void</span> <span class=nf>setSeckillId</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>long</span> <span class=nf>getNow</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=n>now</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>void</span> <span class=nf>setNow</span><span class=o>(</span><span class=kt>long</span> <span class=n>now</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>now</span> <span class=o>=</span> <span class=n>now</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>long</span> <span class=nf>getStart</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=n>start</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>void</span> <span class=nf>setStart</span><span class=o>(</span><span class=kt>long</span> <span class=n>start</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>start</span> <span class=o>=</span> <span class=n>start</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>long</span> <span class=nf>getEnd</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=n>end</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=kd>public</span> <span class=kt>void</span> <span class=nf>setEnd</span><span class=o>(</span><span class=kt>long</span> <span class=n>end</span><span class=o>)</span> <span class=o>{</span>
			<span class=k>this</span><span class=o>.</span><span class=na>end</span> <span class=o>=</span> <span class=n>end</span><span class=o>;</span>
		<span class=o>}</span>
	
		<span class=nd>@Override</span>
		<span class=kd>public</span> <span class=n>String</span> <span class=nf>toString</span><span class=o>()</span> <span class=o>{</span>
			<span class=k>return</span> <span class=s>&#34;Exposer{&#34;</span> <span class=o>+</span> <span class=s>&#34;exposed=&#34;</span> <span class=o>+</span> <span class=n>exposed</span> <span class=o>+</span> <span class=s>&#34;, md5=&#39;&#34;</span> <span class=o>+</span> <span class=n>md5</span> <span class=o>+</span> <span class=sc>&#39;\&#39;&#39;</span> <span class=o>+</span> <span class=s>&#34;, seckillId=&#34;</span> <span class=o>+</span> <span class=n>seckillId</span> <span class=o>+</span> <span class=s>&#34;, now=&#34;</span> <span class=o>+</span> <span class=n>now</span>
					<span class=o>+</span> <span class=s>&#34;, start=&#34;</span> <span class=o>+</span> <span class=n>start</span> <span class=o>+</span> <span class=s>&#34;, end=&#34;</span> <span class=o>+</span> <span class=n>end</span> <span class=o>+</span> <span class=sc>&#39;}&#39;</span><span class=o>;</span>
		<span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>在dto包中创建SeckillExecution.java，用于封装秒杀是否成功的结果（该对象用来返回给页面）</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span><span class=lnt>27
</span><span class=lnt>28
</span><span class=lnt>29
</span><span class=lnt>30
</span><span class=lnt>31
</span><span class=lnt>32
</span><span class=lnt>33
</span><span class=lnt>34
</span><span class=lnt>35
</span><span class=lnt>36
</span><span class=lnt>37
</span><span class=lnt>38
</span><span class=lnt>39
</span><span class=lnt>40
</span><span class=lnt>41
</span><span class=lnt>42
</span><span class=lnt>43
</span><span class=lnt>44
</span><span class=lnt>45
</span><span class=lnt>46
</span><span class=lnt>47
</span><span class=lnt>48
</span><span class=lnt>49
</span><span class=lnt>50
</span><span class=lnt>51
</span><span class=lnt>52
</span><span class=lnt>53
</span><span class=lnt>54
</span><span class=lnt>55
</span><span class=lnt>56
</span><span class=lnt>57
</span><span class=lnt>58
</span><span class=lnt>59
</span><span class=lnt>60
</span><span class=lnt>61
</span><span class=lnt>62
</span><span class=lnt>63
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=cm>/**
</span><span class=cm>	 * 封装执行秒杀后的结果:是否秒杀成功
</span><span class=cm>	 */</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>SeckillExecution</span> <span class=o>{</span>
	
	    <span class=kd>private</span> <span class=kt>long</span> <span class=n>seckillId</span><span class=o>;</span>
	
	    <span class=c1>//秒杀执行结果的状态
</span><span class=c1></span>	    <span class=kd>private</span> <span class=kt>int</span> <span class=n>state</span><span class=o>;</span>
	
	    <span class=c1>//状态的明文标识
</span><span class=c1></span>	    <span class=kd>private</span> <span class=n>String</span> <span class=n>stateInfo</span><span class=o>;</span>
	
	    <span class=c1>//当秒杀成功时，需要传递秒杀成功的对象回去
</span><span class=c1></span>	    <span class=kd>private</span> <span class=n>SuccessKilled</span> <span class=n>successKilled</span><span class=o>;</span>
	
	    <span class=c1>//秒杀成功返回所有信息
</span><span class=c1></span>	    <span class=kd>public</span> <span class=nf>SeckillExecution</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=kt>int</span> <span class=n>state</span><span class=o>,</span> <span class=n>String</span> <span class=n>stateInfo</span><span class=o>,</span> <span class=n>SuccessKilled</span> <span class=n>successKilled</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>state</span> <span class=o>=</span> <span class=n>state</span><span class=o>;</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>stateInfo</span> <span class=o>=</span> <span class=n>stateInfo</span><span class=o>;</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>successKilled</span> <span class=o>=</span> <span class=n>successKilled</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=c1>//秒杀失败
</span><span class=c1></span>	    <span class=kd>public</span> <span class=nf>SeckillExecution</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=kt>int</span> <span class=n>state</span><span class=o>,</span> <span class=n>String</span> <span class=n>stateInfo</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>state</span> <span class=o>=</span> <span class=n>state</span><span class=o>;</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>stateInfo</span> <span class=o>=</span> <span class=n>stateInfo</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>long</span> <span class=nf>getSeckillId</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>seckillId</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>setSeckillId</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>int</span> <span class=nf>getState</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>state</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>setState</span><span class=o>(</span><span class=kt>int</span> <span class=n>state</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>state</span> <span class=o>=</span> <span class=n>state</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=n>String</span> <span class=nf>getStateInfo</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>stateInfo</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>setStateInfo</span><span class=o>(</span><span class=n>String</span> <span class=n>stateInfo</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>stateInfo</span> <span class=o>=</span> <span class=n>stateInfo</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=n>SuccessKilled</span> <span class=nf>getSuccessKilled</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>successKilled</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>setSuccessKilled</span><span class=o>(</span><span class=n>SuccessKilled</span> <span class=n>successKilled</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>successKilled</span> <span class=o>=</span> <span class=n>successKilled</span><span class=o>;</span>
	    <span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>在exception包中创建秒杀过程中可能出现的异常类</p><blockquote><p><strong>定义一个基础的异常类SeckillException，继承自RuntimeException</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=cm>/**
</span><span class=cm>	 * 秒杀相关的所有业务异常
</span><span class=cm>	 */</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>SeckillException</span> <span class=kd>extends</span> <span class=n>RuntimeException</span> <span class=o>{</span>
	    <span class=kd>public</span> <span class=nf>SeckillException</span><span class=o>(</span><span class=n>String</span> <span class=n>message</span><span class=o>)</span> <span class=o>{</span>
	        <span class=kd>super</span><span class=o>(</span><span class=n>message</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=nf>SeckillException</span><span class=o>(</span><span class=n>String</span> <span class=n>message</span><span class=o>,</span> <span class=n>Throwable</span> <span class=n>cause</span><span class=o>)</span> <span class=o>{</span>
	        <span class=kd>super</span><span class=o>(</span><span class=n>message</span><span class=o>,</span> <span class=n>cause</span><span class=o>);</span>
	    <span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>重复秒杀异常，继承自SeckillException</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=cm>/**
</span><span class=cm>	 * 重复秒杀异常，是一个运行期异常，不需要我们手动try catch
</span><span class=cm>	 * Mysql只支持运行期异常的回滚操作
</span><span class=cm>	 */</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>RepeatKillException</span> <span class=kd>extends</span> <span class=n>SeckillException</span> <span class=o>{</span>
	
	    <span class=kd>public</span> <span class=nf>RepeatKillException</span><span class=o>(</span><span class=n>String</span> <span class=n>message</span><span class=o>)</span> <span class=o>{</span>
	        <span class=kd>super</span><span class=o>(</span><span class=n>message</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=nf>RepeatKillException</span><span class=o>(</span><span class=n>String</span> <span class=n>message</span><span class=o>,</span> <span class=n>Throwable</span> <span class=n>cause</span><span class=o>)</span> <span class=o>{</span>
	        <span class=kd>super</span><span class=o>(</span><span class=n>message</span><span class=o>,</span> <span class=n>cause</span><span class=o>);</span>
	    <span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>秒杀已关闭异常，继承自SeckillException</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=cm>/**
</span><span class=cm>	 * 秒杀关闭异常，当秒杀结束时用户还要进行秒杀就会出现这个异常
</span><span class=cm>	 */</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>SeckillCloseException</span> <span class=kd>extends</span> <span class=n>SeckillException</span><span class=o>{</span>
	    <span class=kd>public</span> <span class=nf>SeckillCloseException</span><span class=o>(</span><span class=n>String</span> <span class=n>message</span><span class=o>)</span> <span class=o>{</span>
	        <span class=kd>super</span><span class=o>(</span><span class=n>message</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=nf>SeckillCloseException</span><span class=o>(</span><span class=n>String</span> <span class=n>message</span><span class=o>,</span> <span class=n>Throwable</span> <span class=n>cause</span><span class=o>)</span> <span class=o>{</span>
	        <span class=kd>super</span><span class=o>(</span><span class=n>message</span><span class=o>,</span> <span class=n>cause</span><span class=o>);</span>
	    <span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><h2 id=3-service层接口的实现><strong>3. Service层接口的实现</strong></h2><p>在<code>com.lewis.service</code>包下再建立<code>impl</code>包，用来存放接口的实现类<code>SeckillServiceImpl</code></p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span><span class=lnt>27
</span><span class=lnt>28
</span><span class=lnt>29
</span><span class=lnt>30
</span><span class=lnt>31
</span><span class=lnt>32
</span><span class=lnt>33
</span><span class=lnt>34
</span><span class=lnt>35
</span><span class=lnt>36
</span><span class=lnt>37
</span><span class=lnt>38
</span><span class=lnt>39
</span><span class=lnt>40
</span><span class=lnt>41
</span><span class=lnt>42
</span><span class=lnt>43
</span><span class=lnt>44
</span><span class=lnt>45
</span><span class=lnt>46
</span><span class=lnt>47
</span><span class=lnt>48
</span><span class=lnt>49
</span><span class=lnt>50
</span><span class=lnt>51
</span><span class=lnt>52
</span><span class=lnt>53
</span><span class=lnt>54
</span><span class=lnt>55
</span><span class=lnt>56
</span><span class=lnt>57
</span><span class=lnt>58
</span><span class=lnt>59
</span><span class=lnt>60
</span><span class=lnt>61
</span><span class=lnt>62
</span><span class=lnt>63
</span><span class=lnt>64
</span><span class=lnt>65
</span><span class=lnt>66
</span><span class=lnt>67
</span><span class=lnt>68
</span><span class=lnt>69
</span><span class=lnt>70
</span><span class=lnt>71
</span><span class=lnt>72
</span><span class=lnt>73
</span><span class=lnt>74
</span><span class=lnt>75
</span><span class=lnt>76
</span><span class=lnt>77
</span><span class=lnt>78
</span><span class=lnt>79
</span><span class=lnt>80
</span><span class=lnt>81
</span><span class=lnt>82
</span><span class=lnt>83
</span><span class=lnt>84
</span><span class=lnt>85
</span><span class=lnt>86
</span><span class=lnt>87
</span><span class=lnt>88
</span><span class=lnt>89
</span><span class=lnt>90
</span><span class=lnt>91
</span><span class=lnt>92
</span><span class=lnt>93
</span><span class=lnt>94
</span><span class=lnt>95
</span><span class=lnt>96
</span><span class=lnt>97
</span><span class=lnt>98
</span><span class=lnt>99
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>SeckillServiceImpl</span> <span class=kd>implements</span> <span class=n>SeckillService</span>
	<span class=o>{</span>
	    <span class=c1>//日志对象
</span><span class=c1></span>	    <span class=kd>private</span> <span class=n>Logger</span> <span class=n>logger</span><span class=o>=</span> <span class=n>LoggerFactory</span><span class=o>.</span><span class=na>getLogger</span><span class=o>(</span><span class=k>this</span><span class=o>.</span><span class=na>getClass</span><span class=o>());</span>
	
	    <span class=c1>//加入一个混淆字符串(秒杀接口)的salt，为了我避免用户猜出我们的md5值，值任意给，越复杂越好
</span><span class=c1></span>	    <span class=kd>private</span> <span class=kd>final</span> <span class=n>String</span> <span class=n>salt</span><span class=o>=</span><span class=s>&#34;aksehiucka24sf*&amp;%&amp;^^#^%$&#34;</span><span class=o>;</span>
	
	    <span class=c1>//注入Service依赖
</span><span class=c1></span>	    <span class=nd>@Autowired</span> <span class=c1>//@Resource
</span><span class=c1></span>	    <span class=kd>private</span> <span class=n>SeckillDao</span> <span class=n>seckillDao</span><span class=o>;</span>
	
	    <span class=nd>@Autowired</span> <span class=c1>//@Resource
</span><span class=c1></span>	    <span class=kd>private</span> <span class=n>SuccessKilledDao</span> <span class=n>successKilledDao</span><span class=o>;</span>
	
	    <span class=kd>public</span> <span class=n>List</span><span class=o>&lt;</span><span class=n>Seckill</span><span class=o>&gt;</span> <span class=nf>getSeckillList</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>seckillDao</span><span class=o>.</span><span class=na>queryAll</span><span class=o>(</span><span class=n>0</span><span class=o>,</span><span class=n>4</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=n>Seckill</span> <span class=nf>getById</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>seckillDao</span><span class=o>.</span><span class=na>queryById</span><span class=o>(</span><span class=n>seckillId</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=n>Exposer</span> <span class=nf>exportSeckillUrl</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span> <span class=o>{</span>
	        <span class=n>Seckill</span> <span class=n>seckill</span><span class=o>=</span><span class=n>seckillDao</span><span class=o>.</span><span class=na>queryById</span><span class=o>(</span><span class=n>seckillId</span><span class=o>);</span>
	        <span class=k>if</span> <span class=o>(</span><span class=n>seckill</span><span class=o>==</span><span class=kc>null</span><span class=o>)</span> <span class=c1>//说明查不到这个秒杀产品的记录
</span><span class=c1></span>	        <span class=o>{</span>
	            <span class=k>return</span> <span class=k>new</span> <span class=n>Exposer</span><span class=o>(</span><span class=kc>false</span><span class=o>,</span><span class=n>seckillId</span><span class=o>);</span>
	        <span class=o>}</span>
	
	        <span class=c1>//若是秒杀未开启
</span><span class=c1></span>	        <span class=n>Date</span> <span class=n>startTime</span><span class=o>=</span><span class=n>seckill</span><span class=o>.</span><span class=na>getStartTime</span><span class=o>();</span>
	        <span class=n>Date</span> <span class=n>endTime</span><span class=o>=</span><span class=n>seckill</span><span class=o>.</span><span class=na>getEndTime</span><span class=o>();</span>
	        <span class=c1>//系统当前时间
</span><span class=c1></span>	        <span class=n>Date</span> <span class=n>nowTime</span><span class=o>=</span><span class=k>new</span> <span class=n>Date</span><span class=o>();</span>
	        <span class=k>if</span> <span class=o>(</span><span class=n>startTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>()&gt;</span><span class=n>nowTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>()</span> <span class=o>||</span> <span class=n>endTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>()&lt;</span><span class=n>nowTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>())</span>
	        <span class=o>{</span>
	            <span class=k>return</span> <span class=k>new</span> <span class=n>Exposer</span><span class=o>(</span><span class=kc>false</span><span class=o>,</span><span class=n>seckillId</span><span class=o>,</span><span class=n>nowTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>(),</span><span class=n>startTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>(),</span><span class=n>endTime</span><span class=o>.</span><span class=na>getTime</span><span class=o>());</span>
	        <span class=o>}</span>
	
	        <span class=c1>//秒杀开启，返回秒杀商品的id、用给接口加密的md5
</span><span class=c1></span>	        <span class=n>String</span> <span class=n>md5</span><span class=o>=</span><span class=n>getMD5</span><span class=o>(</span><span class=n>seckillId</span><span class=o>);</span>
	        <span class=k>return</span> <span class=k>new</span> <span class=n>Exposer</span><span class=o>(</span><span class=kc>true</span><span class=o>,</span><span class=n>md5</span><span class=o>,</span><span class=n>seckillId</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=kd>private</span> <span class=n>String</span> <span class=nf>getMD5</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>)</span>
	    <span class=o>{</span>
	        <span class=n>String</span> <span class=n>base</span><span class=o>=</span><span class=n>seckillId</span><span class=o>+</span><span class=s>&#34;/&#34;</span><span class=o>+</span><span class=n>salt</span><span class=o>;</span>
	        <span class=n>String</span> <span class=n>md5</span><span class=o>=</span> <span class=n>DigestUtils</span><span class=o>.</span><span class=na>md5DigestAsHex</span><span class=o>(</span><span class=n>base</span><span class=o>.</span><span class=na>getBytes</span><span class=o>());</span>
	        <span class=k>return</span> <span class=n>md5</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=c1>//秒杀是否成功，成功:减库存，增加明细；失败:抛出异常，事务回滚
</span><span class=c1></span>	    <span class=kd>public</span> <span class=n>SeckillExecution</span> <span class=nf>executeSeckill</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=kt>long</span> <span class=n>userPhone</span><span class=o>,</span> <span class=n>String</span> <span class=n>md5</span><span class=o>)</span>
	            <span class=kd>throws</span> <span class=n>SeckillException</span><span class=o>,</span> <span class=n>RepeatKillException</span><span class=o>,</span> <span class=n>SeckillCloseException</span> <span class=o>{</span>
	
	        <span class=k>if</span> <span class=o>(</span><span class=n>md5</span><span class=o>==</span><span class=kc>null</span><span class=o>||!</span><span class=n>md5</span><span class=o>.</span><span class=na>equals</span><span class=o>(</span><span class=n>getMD5</span><span class=o>(</span><span class=n>seckillId</span><span class=o>)))</span>
	        <span class=o>{</span>
	            <span class=k>throw</span> <span class=k>new</span> <span class=n>SeckillException</span><span class=o>(</span><span class=s>&#34;seckill data rewrite&#34;</span><span class=o>);</span><span class=c1>//秒杀数据被重写了
</span><span class=c1></span>	        <span class=o>}</span>
	        <span class=c1>//执行秒杀逻辑:减库存+增加购买明细
</span><span class=c1></span>	        <span class=n>Date</span> <span class=n>nowTime</span><span class=o>=</span><span class=k>new</span> <span class=n>Date</span><span class=o>();</span>
	
	        <span class=k>try</span><span class=o>{</span>
	            <span class=c1>//减库存
</span><span class=c1></span>	            <span class=kt>int</span> <span class=n>updateCount</span><span class=o>=</span><span class=n>seckillDao</span><span class=o>.</span><span class=na>reduceNumber</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span><span class=n>nowTime</span><span class=o>);</span>
	            <span class=k>if</span> <span class=o>(</span><span class=n>updateCount</span><span class=o>&lt;=</span><span class=n>0</span><span class=o>)</span>
	            <span class=o>{</span>
	                <span class=c1>//没有更新库存记录，说明秒杀结束
</span><span class=c1></span>	                <span class=k>throw</span> <span class=k>new</span> <span class=n>SeckillCloseException</span><span class=o>(</span><span class=s>&#34;seckill is closed&#34;</span><span class=o>);</span>
	            <span class=o>}</span><span class=k>else</span> <span class=o>{</span>
	                <span class=c1>//否则更新了库存，秒杀成功,增加明细
</span><span class=c1></span>	                <span class=kt>int</span> <span class=n>insertCount</span><span class=o>=</span><span class=n>successKilledDao</span><span class=o>.</span><span class=na>insertSuccessKilled</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span><span class=n>userPhone</span><span class=o>);</span>
	                <span class=c1>//看是否该明细被重复插入，即用户是否重复秒杀
</span><span class=c1></span>	                <span class=k>if</span> <span class=o>(</span><span class=n>insertCount</span><span class=o>&lt;=</span><span class=n>0</span><span class=o>)</span>
	                <span class=o>{</span>
	                    <span class=k>throw</span> <span class=k>new</span> <span class=n>RepeatKillException</span><span class=o>(</span><span class=s>&#34;seckill repeated&#34;</span><span class=o>);</span>
	                <span class=o>}</span><span class=k>else</span> <span class=o>{</span>
	                    <span class=c1>//秒杀成功,得到成功插入的明细记录,并返回成功秒杀的信息
</span><span class=c1></span>	                    <span class=n>SuccessKilled</span> <span class=n>successKilled</span><span class=o>=</span><span class=n>successKilledDao</span><span class=o>.</span><span class=na>queryByIdWithSeckill</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span><span class=n>userPhone</span><span class=o>);</span>
	                    <span class=k>return</span> <span class=k>new</span> <span class=n>SeckillExecution</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span><span class=n>1</span><span class=o>,</span><span class=s>&#34;秒杀成功&#34;</span><span class=o>,</span><span class=n>successKilled</span><span class=o>);</span>
	                <span class=o>}</span>
	            <span class=o>}</span>
	
	        <span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>SeckillCloseException</span> <span class=n>e1</span><span class=o>)</span>
	        <span class=o>{</span>
	            <span class=k>throw</span> <span class=n>e1</span><span class=o>;</span>
	        <span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>RepeatKillException</span> <span class=n>e2</span><span class=o>)</span>
	        <span class=o>{</span>
	            <span class=k>throw</span> <span class=n>e2</span><span class=o>;</span>
	        <span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>Exception</span> <span class=n>e</span><span class=o>)</span>
	        <span class=o>{</span>
	            <span class=n>logger</span><span class=o>.</span><span class=na>error</span><span class=o>(</span><span class=n>e</span><span class=o>.</span><span class=na>getMessage</span><span class=o>(),</span><span class=n>e</span><span class=o>);</span>
	            <span class=c1>//将编译期异常转化为运行期异常
</span><span class=c1></span>	            <span class=k>throw</span> <span class=k>new</span> <span class=n>SeckillException</span><span class=o>(</span><span class=s>&#34;seckill inner error :&#34;</span><span class=o>+</span><span class=n>e</span><span class=o>.</span><span class=na>getMessage</span><span class=o>());</span>
	        <span class=o>}</span>
	
	    <span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>在以上代码中，我们捕获了运行时异常，原因是Spring的事务默认是发生了RuntimeException才会回滚，发生了其他异常不会回滚，所以在最后的catch块里通过<code>throw new SeckillException("seckill inner error :"+e.getMessage());</code>将编译期异常转化为运行期异常。</p><p>另外，在代码里还存在着硬编码的情况，比如秒杀结果返回的state和stateInfo参数信息是输出给前端的，这些字符串应该考虑用常量枚举类封装起来，方便重复利用，也易于维护。</p><blockquote><p><strong>在<code>src/main/java</code>包下新建一个枚举包<code>com.lewis.enums</code>包，在该包下创建一个枚举类型SeckillStatEnum</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span><span class=lnt>27
</span><span class=lnt>28
</span><span class=lnt>29
</span><span class=lnt>30
</span><span class=lnt>31
</span><span class=lnt>32
</span><span class=lnt>33
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=kd>public</span> <span class=kd>enum</span> <span class=n>SeckillStatEnum</span> <span class=o>{</span>
	
	    <span class=n>SUCCESS</span><span class=o>(</span><span class=n>1</span><span class=o>,</span><span class=s>&#34;秒杀成功&#34;</span><span class=o>),</span>
	    <span class=n>END</span><span class=o>(</span><span class=n>0</span><span class=o>,</span><span class=s>&#34;秒杀结束&#34;</span><span class=o>),</span>
	    <span class=n>REPEAT_KILL</span><span class=o>(-</span><span class=n>1</span><span class=o>,</span><span class=s>&#34;重复秒杀&#34;</span><span class=o>),</span>
	    <span class=n>INNER_ERROR</span><span class=o>(-</span><span class=n>2</span><span class=o>,</span><span class=s>&#34;系统异常&#34;</span><span class=o>),</span>
	    <span class=n>DATE_REWRITE</span><span class=o>(-</span><span class=n>3</span><span class=o>,</span><span class=s>&#34;数据篡改&#34;</span><span class=o>);</span>
	
	    <span class=kd>private</span> <span class=kt>int</span> <span class=n>state</span><span class=o>;</span>
	    <span class=kd>private</span> <span class=n>String</span> <span class=n>info</span><span class=o>;</span>
	
	    <span class=n>SeckillStatEnum</span><span class=o>(</span><span class=kt>int</span> <span class=n>state</span><span class=o>,</span> <span class=n>String</span> <span class=n>info</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>state</span> <span class=o>=</span> <span class=n>state</span><span class=o>;</span>
	        <span class=k>this</span><span class=o>.</span><span class=na>info</span> <span class=o>=</span> <span class=n>info</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kt>int</span> <span class=nf>getState</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>state</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=n>String</span> <span class=nf>getInfo</span><span class=o>()</span> <span class=o>{</span>
	        <span class=k>return</span> <span class=n>info</span><span class=o>;</span>
	    <span class=o>}</span>
	
	    <span class=kd>public</span> <span class=kd>static</span> <span class=n>SeckillStatEnum</span> <span class=nf>stateOf</span><span class=o>(</span><span class=kt>int</span> <span class=n>index</span><span class=o>)</span> <span class=o>{</span>
	        <span class=k>for</span> <span class=o>(</span><span class=n>SeckillStatEnum</span> <span class=n>state</span> <span class=o>:</span> <span class=n>values</span><span class=o>())</span> <span class=o>{</span>
	            <span class=k>if</span> <span class=o>(</span><span class=n>state</span><span class=o>.</span><span class=na>getState</span><span class=o>()==</span><span class=n>index</span><span class=o>)</span> <span class=o>{</span>
	                <span class=k>return</span> <span class=n>state</span><span class=o>;</span>
	            <span class=o>}</span>
	        <span class=o>}</span>
			<span class=k>return</span> <span class=kc>null</span><span class=o>;</span>
		<span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>创建了枚举类型后，就需要修改之前硬编码的地方，修改<code>SeckillExecution</code>涉及到state和stateInfo参数的构造方法</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=c1>//秒杀成功返回所有信息
</span><span class=c1></span>	 <span class=kd>public</span> <span class=nf>SeckillExecution</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=n>SeckillStatEnum</span> <span class=n>statEnum</span><span class=o>,</span> <span class=n>SuccessKilled</span> <span class=n>successKilled</span><span class=o>)</span> <span class=o>{</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>state</span> <span class=o>=</span> <span class=n>statEnum</span><span class=o>.</span><span class=na>getState</span><span class=o>();</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>stateInfo</span> <span class=o>=</span> <span class=n>statEnum</span><span class=o>.</span><span class=na>getInfo</span><span class=o>();</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>successKilled</span> <span class=o>=</span> <span class=n>successKilled</span><span class=o>;</span>
	 <span class=o>}</span>
	
	 <span class=c1>//秒杀失败
</span><span class=c1></span>	 <span class=kd>public</span> <span class=nf>SeckillExecution</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=n>SeckillStatEnum</span> <span class=n>statEnum</span><span class=o>)</span> <span class=o>{</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>seckillId</span> <span class=o>=</span> <span class=n>seckillId</span><span class=o>;</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>state</span> <span class=o>=</span> <span class=n>statEnum</span><span class=o>.</span><span class=na>getState</span><span class=o>();</span>
	     <span class=k>this</span><span class=o>.</span><span class=na>stateInfo</span> <span class=o>=</span> <span class=n>statEnum</span><span class=o>.</span><span class=na>getInfo</span><span class=o>();</span>
	 <span class=o>}</span>
</code></pre></td></tr></table></div></div><p>接着把<code>SeckillServiceImpl</code>里返回的秒杀成功信息的<code>return new SeckillExecution(seckillId,1,"秒杀成功",successKilled);</code>改成<code>return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS,successKilled);</code></p><h2 id=4-使用spring进行service层的配置>4. 使用Spring进行Service层的配置</h2><blockquote><p><strong>在之前创建的<code>spring</code>包下创建spring-service.xml</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml>	<span class=cp>&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
	<span class=nt>&lt;beans</span> <span class=na>xmlns=</span><span class=s>&#34;http://www.springframework.org/schema/beans&#34;</span>
		<span class=na>xmlns:xsi=</span><span class=s>&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span> 
		<span class=na>xmlns:context=</span><span class=s>&#34;http://www.springframework.org/schema/context&#34;</span>
		<span class=na>xmlns:tx=</span><span class=s>&#34;http://www.springframework.org/schema/tx&#34;</span>
		<span class=na>xsi:schemaLocation=</span><span class=s>&#34;http://www.springframework.org/schema/beans
</span><span class=s>	        http://www.springframework.org/schema/beans/spring-beans.xsd
</span><span class=s>	        http://www.springframework.org/schema/context 
</span><span class=s>	        http://www.springframework.org/schema/context/spring-context.xsd 
</span><span class=s>	        http://www.springframework.org/schema/tx 
</span><span class=s>	        http://www.springframework.org/schema/tx/spring-tx.xsd&#34;</span><span class=nt>&gt;</span>
	
		<span class=c>&lt;!--扫描service包下所有使用注解的类型 --&gt;</span>
		<span class=nt>&lt;context:component-scan</span> <span class=na>base-package=</span><span class=s>&#34;com.lewis.service&#34;</span> <span class=nt>/&gt;</span>
	
		<span class=c>&lt;!--配置事务管理器 --&gt;</span>
		<span class=nt>&lt;bean</span> <span class=na>id=</span><span class=s>&#34;transactionManager&#34;</span>
			<span class=na>class=</span><span class=s>&#34;org.springframework.jdbc.datasource.DataSourceTransactionManager&#34;</span><span class=nt>&gt;</span>
			<span class=c>&lt;!--注入数据库连接池 --&gt;</span>
			<span class=nt>&lt;property</span> <span class=na>name=</span><span class=s>&#34;dataSource&#34;</span> <span class=na>ref=</span><span class=s>&#34;dataSource&#34;</span> <span class=nt>/&gt;</span>
		<span class=nt>&lt;/bean&gt;</span>
	
		<span class=c>&lt;!--配置基于注解的声明式事务 默认使用注解来管理事务行为 --&gt;</span>
		<span class=nt>&lt;tx:annotation-driven</span> <span class=na>transaction-manager=</span><span class=s>&#34;transactionManager&#34;</span> <span class=nt>/&gt;</span>
	
	<span class=nt>&lt;/beans&gt;</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>事务管理器</strong></p></blockquote><p>MyBatis采用的是JDBC的事务管理器</p><p>Hibernate采用的是Hibernate的事务管理器</p><blockquote><p><strong>通过注解的方式将Service的实现类（注意，不是Service接口）加入到Spring IoC容器中</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Service</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>SeckillServiceImpl</span> <span class=kd>implements</span> <span class=n>SeckillService</span><span class=o>;</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>在需要进行事务声明的方法上加上事务的注解@Transactional</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Transactional</span>
	<span class=kd>public</span> <span class=n>SeckillExecution</span> <span class=nf>executeSeckill</span><span class=o>(</span><span class=kt>long</span> <span class=n>seckillId</span><span class=o>,</span> <span class=kt>long</span> <span class=n>userPhone</span><span class=o>,</span> <span class=n>String</span> <span class=n>md5</span><span class=o>)</span>
			<span class=kd>throws</span> <span class=n>SeckillException</span><span class=o>,</span> <span class=n>RepeatKillException</span><span class=o>,</span> <span class=n>SeckillCloseException</span> <span class=o>{}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>Spring的声明式事务管理</strong></p></blockquote><ul><li>异常捕获机制</li></ul><p>Java异常分编译期异常和运行期异常，运行期异常不需要手工try-catch，Spring的的声明式事务只接收运行期异常回滚策略，非运行期异常不会帮我们回滚。</p><ul><li>事务传播行为</li></ul><p>Spring一共有7个事务传播行为，默认的事务传播行为是<code>PROPAGATION_REQUIRED</code>，详情可以参考<a href=http://blog.csdn.net/it_wangxiangpan/article/details/24180085 target=_blank rel="noopener noreffer">这篇文章</a></p><blockquote><p><strong>使用注解控制事务方法的优点（对于秒杀这种对事务延迟要求高的业务场景尤为重要）</strong></p></blockquote><ul><li>1.开发团队达成一致约定，明确标注事务方法的编程风格</li><li>2.保证事务方法的执行时间尽可能短，不要穿插其他网络操作RPC/HTTP请求或者剥离到事务方法外部（保证事务方法里面是很干净的/效率的）</li><li>3.不是所有的方法都需要事务，如只有一条修改操作、只读操作不要事务控制（MYSQL 表级锁、行级锁）</li></ul><blockquote><p><strong>为什么使用IoC（控制反转）</strong></p></blockquote><ol><li>对象创建统一托管。</li><li>规范的生命周期管理。</li><li>灵活的依赖注入。</li><li>一致的对象获取方式。</li></ol><blockquote><p><strong>Spring基于注解的事务操作</strong></p></blockquote><ul><li>在Spring早期版本中是使用ProxyFactoryBean+XMl方式来配置事务。</li><li>在Spring配置文件使用tx:advice+aop命名空间，好处就是一次配置永久生效，你无须去关心中间出的问题，不过出错了你很难找出来在哪里出了问题。</li><li>注解@Transactional的方式，注解可以在方法定义、接口定义、类定义、public方法上，但是不能注解在private、final、static等方法上，因为Spring的事务管理默认是使用Cglib动态代理的：<ul><li>private方法因为访问权限限制，无法被子类覆盖</li><li>final方法无法被子类覆盖</li><li>static是类级别的方法，无法被子类覆盖</li><li>protected方法可以被子类覆盖，因此可以被动态字节码增强</li></ul></li></ul><blockquote><p><strong>不能被Spring AOP事务增强的方法</strong></p></blockquote><table><thead><tr><th>序号</th><th>动态代理策略</th><th>不能被事务增强的方法</th></tr></thead><tbody><tr><td>1</td><td>基于接口的动态代理</td><td>除了public以外的所有方法，并且public static的方法也不能被增强</td></tr><tr><td>2</td><td>基于Cglib的动态代理</td><td>private、static、final的方法</td></tr></tbody></table><blockquote><p><strong>关于Spring的组件注解、注入注解</strong></p></blockquote><ul><li>@Component：标识一个组件，当不知道是什么组件，或者该组件不好归类时使用该注解</li><li>@Service：标识业务层组件</li><li>@Repository：标识DAO层组件</li><li>@Controller：标识控制层组件</li></ul><p>通过Spring提供的组件自动扫描机制，可以在类路径下寻找标注了上述注解的类，并把这些类纳入进spring容器中管理，这些注解的作用和在xml文件中使用bean节点配置组件时一样的。</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml>	<span class=nt>&lt;context:component-scan</span> <span class=na>base-package=</span><span class=s>”xxx.xxx.xxx”</span><span class=nt>&gt;</span>
</code></pre></td></tr></table></div></div><p><code>component-scan</code>标签默认情况下自动扫描指定路径下的包(含所有子包)，将带有@Component、@Repository、@Service、@Controller标签的类自动注册到spring容器。getBean的默认名称是类名（头字母小写），如果想自定义，可以@Service(“aaaaa”)这样来指定。这种bean默认是“singleton”的，如果想改变，可以使用@Scope(“prototype”)来改变。</p><p>当使用<code>&lt;context:component-scan/></code>后，就可以将<code>&lt;context:annotation-config/></code>移除了，前者包含了后者。</p><p>另外，@Resource，@Inject 是J2EE规范的一些注解</p><p>@Autowired是Spring的注解，可以对类成员变量、方法及构造函数进行标注，完成自动装配的工作。通过 @Autowired的使用来消除setter/getter方法，默认按类型装配，如果想使用名称装配可以结合@Qualifier注解进行使用，如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Autowired</span><span class=o>()</span> <span class=nd>@Qualifier</span><span class=o>(</span><span class=s>&#34;baseDao&#34;</span><span class=o>)</span>     
	<span class=kd>private</span> <span class=n>BaseDao</span> <span class=n>baseDao</span><span class=o>;</span> 
</code></pre></td></tr></table></div></div><p>与@Autowired类似的是@Resource，@Resource属于J2EE规范，默认安照名称进行装配，名称可以通过name属性进行指定，如果没有指定name属性，当注解写在字段上时，默认取字段名进行按照名称查找，如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是，如果name属性一旦指定，就只会按照名称进行装配。</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Resource</span><span class=o>(</span><span class=n>name</span><span class=o>=</span><span class=s>&#34;baseDao&#34;</span><span class=o>)</span>     
	<span class=kd>private</span> <span class=n>BaseDao</span> <span class=n>baseDao</span><span class=o>;</span> 
</code></pre></td></tr></table></div></div><p>而@Inject与@Autowired类似，也是根据类型注入，也可以通过@Named注解来按照name注入，此时只会按照名称进行装配。</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Inject</span> <span class=nd>@Named</span><span class=o>(</span><span class=s>&#34;baseDao&#34;</span><span class=o>)</span>
	<span class=kd>private</span> <span class=n>BaseDao</span> <span class=n>baseDao</span><span class=o>;</span> 
</code></pre></td></tr></table></div></div><h2 id=5-进行service层的集成测试><strong>5. 进行Service层的集成测试</strong></h2><blockquote><p><strong>使用logback来输出日志信息，在<code>resources</code>包下创建logback.xml</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml>	<span class=cp>&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
	<span class=nt>&lt;configuration&gt;</span>
	  <span class=nt>&lt;appender</span> <span class=na>name=</span><span class=s>&#34;STDOUT&#34;</span> <span class=na>class=</span><span class=s>&#34;ch.qos.logback.core.ConsoleAppender&#34;</span><span class=nt>&gt;</span>
	    <span class=c>&lt;!-- encoders are assigned the type
</span><span class=c>	         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;</span>
	    <span class=nt>&lt;encoder&gt;</span>
	      <span class=nt>&lt;pattern&gt;</span>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n<span class=nt>&lt;/pattern&gt;</span>
	    <span class=nt>&lt;/encoder&gt;</span>
	  <span class=nt>&lt;/appender&gt;</span>
	
	  <span class=nt>&lt;root</span> <span class=na>level=</span><span class=s>&#34;debug&#34;</span><span class=nt>&gt;</span>
	    <span class=nt>&lt;appender-ref</span> <span class=na>ref=</span><span class=s>&#34;STDOUT&#34;</span> <span class=nt>/&gt;</span>
	  <span class=nt>&lt;/root&gt;</span>
	<span class=nt>&lt;/configuration&gt;</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>通过IDE工具快速生成Junit单元测试，然后在各个方法里写测试代码。</strong></p></blockquote><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@RunWith</span><span class=o>(</span><span class=n>SpringJUnit4ClassRunner</span><span class=o>.</span><span class=na>class</span><span class=o>)</span>
	<span class=c1>//告诉junit spring的配置文件
</span><span class=c1></span>	<span class=nd>@ContextConfiguration</span><span class=o>({</span><span class=s>&#34;classpath:spring/spring-dao.xml&#34;</span><span class=o>,</span>
	                        <span class=s>&#34;classpath:spring/spring-service.xml&#34;</span><span class=o>})</span>
	<span class=kd>public</span> <span class=kd>class</span> <span class=nc>SeckillServiceTest</span> <span class=o>{</span>
	
	    <span class=kd>private</span> <span class=kd>final</span> <span class=n>Logger</span> <span class=n>logger</span><span class=o>=</span> <span class=n>LoggerFactory</span><span class=o>.</span><span class=na>getLogger</span><span class=o>(</span><span class=k>this</span><span class=o>.</span><span class=na>getClass</span><span class=o>());</span>
	
	    <span class=nd>@Autowired</span>
	    <span class=kd>private</span> <span class=n>SeckillService</span> <span class=n>seckillService</span><span class=o>;</span>
	
	    <span class=nd>@Test</span>
	    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>testGetSeckillList</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>Exception</span> <span class=o>{</span>
	        <span class=n>List</span><span class=o>&lt;</span><span class=n>Seckill</span><span class=o>&gt;</span> <span class=n>list</span><span class=o>=</span><span class=n>seckillService</span><span class=o>.</span><span class=na>getSeckillList</span><span class=o>();</span>
	        <span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;list={}&#34;</span><span class=o>,</span> <span class=n>list</span><span class=o>);</span>
	    <span class=o>}</span>
	
	    <span class=nd>@Test</span>
	    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>testGetById</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>Exception</span> <span class=o>{</span>
	        <span class=kt>long</span> <span class=n>seckillId</span><span class=o>=</span><span class=n>1000</span><span class=o>;</span>
	        <span class=n>Seckill</span> <span class=n>seckill</span><span class=o>=</span><span class=n>seckillService</span><span class=o>.</span><span class=na>getById</span><span class=o>(</span><span class=n>seckillId</span><span class=o>);</span>
	        <span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;seckill={}&#34;</span><span class=o>,</span> <span class=n>seckill</span><span class=o>);</span>
	    <span class=o>}</span>

	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>在测试通过了这两个方法后，开始对后两个业务逻辑方法的测试，首先测试<code>testExportSeckillUrl()</code></p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Test</span>
	<span class=kd>public</span> <span class=kt>void</span> <span class=nf>testExportSeckillUrl</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>Exception</span> <span class=o>{</span>
		<span class=kt>long</span> <span class=n>seckillId</span><span class=o>=</span><span class=n>1000</span><span class=o>;</span>
		<span class=n>Exposer</span> <span class=n>exposer</span><span class=o>=</span><span class=n>seckillService</span><span class=o>.</span><span class=na>exportSeckillUrl</span><span class=o>(</span><span class=n>seckillId</span><span class=o>);</span>
		<span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;exposer={}&#34;</span><span class=o>,</span> <span class=n>exposer</span><span class=o>);</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>会发现没有返回商品的秒杀地址，因为我们数据库的秒杀时间和结束秒杀时间没有修改，所以判断当前商品的秒杀已结束。将数据库中的秒杀时间和结束秒杀时间修改成满足我们当前的时间的范围，重新测试该方法，可以获取到该商品的秒杀地址。而第四个方法的测试需要使用到该地址（md5），将该值传入到<code>testExecuteSeckill()</code>中进行测试：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span><span class=lnt>7
</span><span class=lnt>8
</span><span class=lnt>9
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Test</span>
	<span class=kd>public</span> <span class=kt>void</span> <span class=nf>testExecuteSeckill</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>Exception</span> <span class=o>{</span>
		<span class=kt>long</span> <span class=n>seckillId</span><span class=o>=</span><span class=n>1000</span><span class=o>;</span>
		<span class=kt>long</span> <span class=n>userPhone</span><span class=o>=</span><span class=n>13476191876L</span><span class=o>;</span>
		<span class=n>String</span> <span class=n>md5</span><span class=o>=</span><span class=s>&#34;70b9564762568e9ff29a4a949f8f6de4&#34;</span><span class=o>;</span>
		
		<span class=n>SeckillExecution</span> <span class=n>execution</span><span class=o>=</span><span class=n>seckillService</span><span class=o>.</span><span class=na>executeSeckill</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span><span class=n>userPhone</span><span class=o>,</span><span class=n>md5</span><span class=o>);</span>
		<span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;result={}&#34;</span><span class=o>,</span> <span class=n>execution</span><span class=o>);</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>需要注意的是，该方法是会产生异常的，比如我们重复运行该方法，会报错，因为用户进行了重复秒杀，所以我们需要手动try-catch，将程序允许的异常包起来而不去向上抛给junit，更改测试代码如下:</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Test</span>
	<span class=kd>public</span> <span class=kt>void</span> <span class=nf>testExecuteSeckill</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>Exception</span> <span class=o>{</span>
		<span class=kt>long</span> <span class=n>seckillId</span><span class=o>=</span><span class=n>1000</span><span class=o>;</span>
		<span class=kt>long</span> <span class=n>userPhone</span><span class=o>=</span><span class=n>13476191876L</span><span class=o>;</span>
		<span class=n>String</span> <span class=n>md5</span><span class=o>=</span><span class=s>&#34;70b9564762568e9ff29a4a949f8f6de4&#34;</span><span class=o>;</span>
		
		<span class=k>try</span> <span class=o>{</span>
			<span class=n>SeckillExecution</span> <span class=n>execution</span> <span class=o>=</span> <span class=n>seckillService</span><span class=o>.</span><span class=na>executeSeckill</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span> <span class=n>userPhone</span><span class=o>,</span> <span class=n>md5</span><span class=o>);</span>
			<span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;result={}&#34;</span><span class=o>,</span> <span class=n>execution</span><span class=o>);</span>
		<span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>RepeatKillException</span> <span class=n>e</span><span class=o>)</span>
		<span class=o>{</span>
			<span class=n>logger</span><span class=o>.</span><span class=na>error</span><span class=o>(</span><span class=n>e</span><span class=o>.</span><span class=na>getMessage</span><span class=o>());</span>
		<span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>SeckillCloseException</span> <span class=n>e1</span><span class=o>)</span>
		<span class=o>{</span>
			<span class=n>logger</span><span class=o>.</span><span class=na>error</span><span class=o>(</span><span class=n>e1</span><span class=o>.</span><span class=na>getMessage</span><span class=o>());</span>
		<span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>在测试过程中，第四个方法使用到了第三个方法返回的秒杀地址，在实际开发中，我们需要将第三个和第四个方法合并成一个完整逻辑的方法：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span><span class=lnt>26
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=c1>//集成测试代码完整逻辑，注意可重复执行
</span><span class=c1></span>	<span class=nd>@Test</span>
    <span class=kd>public</span> <span class=kt>void</span> <span class=nf>testSeckillLogic</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>Exception</span> <span class=o>{</span>
        <span class=kt>long</span> <span class=n>seckillId</span><span class=o>=</span><span class=n>1000</span><span class=o>;</span>
        <span class=n>Exposer</span> <span class=n>exposer</span><span class=o>=</span><span class=n>seckillService</span><span class=o>.</span><span class=na>exportSeckillUrl</span><span class=o>(</span><span class=n>seckillId</span><span class=o>);</span>
        <span class=k>if</span> <span class=o>(</span><span class=n>exposer</span><span class=o>.</span><span class=na>isExposed</span><span class=o>())</span>
        <span class=o>{</span>
            <span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;exposer={}&#34;</span><span class=o>,</span> <span class=n>exposer</span><span class=o>);</span>
            <span class=kt>long</span> <span class=n>userPhone</span><span class=o>=</span><span class=n>13476191876L</span><span class=o>;</span>
            <span class=n>String</span> <span class=n>md5</span><span class=o>=</span><span class=n>exposer</span><span class=o>.</span><span class=na>getMd5</span><span class=o>();</span>

            <span class=k>try</span> <span class=o>{</span>
                <span class=n>SeckillExecution</span> <span class=n>execution</span> <span class=o>=</span> <span class=n>seckillService</span><span class=o>.</span><span class=na>executeSeckill</span><span class=o>(</span><span class=n>seckillId</span><span class=o>,</span> <span class=n>userPhone</span><span class=o>,</span> <span class=n>md5</span><span class=o>);</span>
                <span class=n>logger</span><span class=o>.</span><span class=na>info</span><span class=o>(</span><span class=s>&#34;result={}&#34;</span><span class=o>,</span> <span class=n>execution</span><span class=o>);</span>
            <span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>RepeatKillException</span> <span class=n>e</span><span class=o>)</span>
            <span class=o>{</span>
                <span class=n>logger</span><span class=o>.</span><span class=na>error</span><span class=o>(</span><span class=n>e</span><span class=o>.</span><span class=na>getMessage</span><span class=o>());</span>
            <span class=o>}</span><span class=k>catch</span> <span class=o>(</span><span class=n>SeckillCloseException</span> <span class=n>e1</span><span class=o>)</span>
            <span class=o>{</span>
                <span class=n>logger</span><span class=o>.</span><span class=na>error</span><span class=o>(</span><span class=n>e1</span><span class=o>.</span><span class=na>getMessage</span><span class=o>());</span>
            <span class=o>}</span>
        <span class=o>}</span><span class=k>else</span> <span class=o>{</span>
            <span class=c1>//秒杀未开启
</span><span class=c1></span>            <span class=n>logger</span><span class=o>.</span><span class=na>warn</span><span class=o>(</span><span class=s>&#34;exposer={}&#34;</span><span class=o>,</span> <span class=n>exposer</span><span class=o>);</span>
        <span class=o>}</span>
    <span class=o>}</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>我们可以在SeckillServiceTest类里面加上@Transational注解，原因是：</strong></p></blockquote><p>@Transactional注解是表明此测试类的事务启用，这样所有的测试方案都会自动的 rollback，即不用自己清除自己所做的任何对数据库的变更了。</p><blockquote><p><strong>日志无法打印的问题</strong></p></blockquote><p>在pom.xml中加上</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span><span class=lnt>7
</span><span class=lnt>8
</span><span class=lnt>9
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml>	<span class=nt>&lt;dependency&gt;</span>

      <span class=nt>&lt;groupId&gt;</span>ch.qos.logback<span class=nt>&lt;/groupId&gt;</span>

      <span class=nt>&lt;artifactId&gt;</span>logback-classic<span class=nt>&lt;/artifactId&gt;</span>

      <span class=nt>&lt;version&gt;</span>1.1.9<span class=nt>&lt;/version&gt;</span>

    <span class=nt>&lt;/dependency&gt;</span>
</code></pre></td></tr></table></div></div><blockquote><p><strong>存在的坑</strong></p></blockquote><ul><li>关于同类中调用事务方法的时候有个坑，同学们需要注意下AOP切不到调用事务方法。事务不会生效，解决办法有几种，可以搜一下，找一下适合自己的方案。本质问题是类内部调用时AOP不会用代理调用内部方法。</li><li>没有引入AOP的xsd会报错</li></ul><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml>		xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance
	
			http://www.springmodules.org/schema/cache/springmodules-cache.xsd 
	
			http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd&#34;
</code></pre></td></tr></table></div></div><blockquote><p><strong>相关链接</strong></p></blockquote><p><a href=http://blog.csdn.net/yipanbo/article/details/46048413 target=_blank rel="noopener noreffer">Spring事务异常回滚，捕获异常不抛出就不会回滚</a></p><blockquote><p><strong>本节结语</strong></p></blockquote><p>至此，关于Java高并发秒杀API的Service层的开发与测试已经完成，接下来进行Web层的开发，详情请参考下一篇文章。</p><p>上一篇文章：<a href=http://blog.csdn.net/lewky_liu/article/details/78159983 target=_blank rel="noopener noreffer"><strong>Java高并发秒杀API(一)之业务分析与DAO层</strong></a></p><p>下一篇文章：<a href=http://blog.csdn.net/lewky_liu/article/details/78162153 target=_blank rel="noopener noreffer"><strong>Java高并发秒杀API(三)之Web层</strong></a></p><div class="details admonition warning open"><div class="details-summary admonition-title"><i class="icon fas fa-exclamation-triangle fa-fwwarning"></i>警告<i class="details-icon fas fa-angle-right fa-fw"></i></div><div class=details-content><div class=admonition-content>本文最后更新于 <span class=timeago datetime=2017-10-05T18:02:28 title="October 5, 2017">October 5, 2017</span>，文中内容可能已过时，请谨慎使用。</div></div></div><div class=post-reward><input type=checkbox name=reward id=reward hidden>
<label class=reward-button for=reward>赞赏支持</label><div class=qr-code><label class=qr-code-image for=reward><img class=image src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/common/wechat.png>
<span>微信打赏</span></label>
<label class=qr-code-image for=reward><img class=image src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/common/alipay.png>
<span>支付宝打赏</span></label></div></div></div><div class=post-footer id=post-footer><div class=post-info><div class=post-info-line><div class=post-info-mod><span>更新于 2017-10-05</span></div><div class=post-info-license><span><a rel="license external nofollow noopener noreffer" href=https://creativecommons.org/licenses/by-nc/4.0/ target=_blank>CC BY-NC 4.0</a></span></div></div><div class=post-info-line><div class=post-info-md><span><a class=link-to-markdown href=/posts/18357.html/index.md target=_blank>阅读原始文档</a></span></div><div class=post-info-share><span><a href=javascript:void(0); title="分享到 Twitter" data-sharer=twitter data-url=https://lewky.cn/posts/18357.html/ data-title=Java高并发秒杀API(二)之Service层 data-hashtags=项目笔记,SSM实战项目,秒杀><i class="fab fa-twitter fa-fw"></i></a><a href=javascript:void(0); title="分享到 Facebook" data-sharer=facebook data-url=https://lewky.cn/posts/18357.html/ data-hashtag=项目笔记><i class="fab fa-facebook-square fa-fw"></i></a><a href=javascript:void(0); title="分享到 Hacker News" data-sharer=hackernews data-url=https://lewky.cn/posts/18357.html/ data-title=Java高并发秒杀API(二)之Service层><i class="fab fa-hacker-news fa-fw"></i></a><a href=javascript:void(0); title="分享到 Line" data-sharer=line data-url=https://lewky.cn/posts/18357.html/ data-title=Java高并发秒杀API(二)之Service层><i data-svg-src=https://cdn.jsdelivr.net/npm/simple-icons@2.14.0/icons/line.svg></i></a><a href=javascript:void(0); title="分享到 微博" data-sharer=weibo data-url=https://lewky.cn/posts/18357.html/ data-title=Java高并发秒杀API(二)之Service层><i class="fab fa-weibo fa-fw"></i></a></span></div></div></div><div class=post-info-more><section class=post-tags><i class="fas fa-tags fa-fw"></i>&nbsp;<a href=/tags/%E9%A1%B9%E7%9B%AE%E7%AC%94%E8%AE%B0/>项目笔记</a>,&nbsp;<a href=/tags/ssm%E5%AE%9E%E6%88%98%E9%A1%B9%E7%9B%AE/>SSM实战项目</a>,&nbsp;<a href=/tags/%E7%A7%92%E6%9D%80/>秒杀</a></section><section><span><a href=javascript:void(0); onclick=window.history.back();>返回</a></span>&nbsp;|&nbsp;<span><a href=/>主页</a></span></section></div><div class=post-nav><a href=/posts/56225.html/ class=prev rel=prev title=Java高并发秒杀API(一)之业务分析与DAO层><i class="fas fa-angle-left fa-fw"></i>Java高并发秒杀API(一)之业务分析与DAO层</a>
<a href=/posts/33818.html/ class=next rel=next title=Java高并发秒杀API(三)之Web层>Java高并发秒杀API(三)之Web层<i class="fas fa-angle-right fa-fw"></i></a></div></div><div id=comments><div id=waline></div><script src=https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js></script><script>new Waline({el:'#waline',meta:["nick","mail","link"],requiredMeta:["nick","mail"],login:"force",placeholder:"为防恶意灌水攻击，评论前需注册并登录，望见谅~",serverURL:"https://comment.lewky.cn/",avatarCDN:"https://sdn.geekzu.org/avatar/",pageSize:20,avatar:"retro",lang:"zh-CN",visitor:true,highlight:true,uploadImage:false,emoji:['https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/嘉然今天吃什么','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/大航海嘉然','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/向晚大魔王','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/贝拉kira','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/珈乐Carol','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/乃琳Queen','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/EveOneCat','https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/滑稽','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/default']});</script></div></article></div></main><footer class=footer><div class=footer-container><div class=footer-line><span id=run-time></span>|
<a href=http://rssblog.vercel.app/ target=_blank rel=noopener title=RSSBlog><i class="fas fa-fw fa-inbox"></i>&nbsp;RSSBlog</a></div><div class=footer-line><i class="far fa-copyright fa-fw"></i><span itemprop=copyrightYear>2018 - 2022</span><span class=author itemprop=copyrightHolder>&nbsp;<a href=/ target=_blank>雨临Lewis</a></span>&nbsp;|&nbsp;<span class=license><a rel="license external nofollow noopener noreffer" href=https://creativecommons.org/licenses/by-nc/4.0/ target=_blank>CC BY-NC 4.0</a></span><span class=icp-splitter>&nbsp;|&nbsp;</span><br class=icp-br><span class=icp><a target=_blank href=http://www.beian.miit.gov.cn/ style=font-weight:700>粤ICP备19103822</a></span></div></div></footer></div><div id=fixed-buttons><a href=# id=back-to-top class=fixed-button title=回到顶部><i class="fas fa-arrow-up fa-fw"></i></a><a href=# id=view-comments class=fixed-button title=查看评论><i class="fas fa-comment fa-fw"></i></a></div><div class=sidebar_wo><div id=leimu><img src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/b2t/leimuA.png alt=雷姆 onmouseover="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/leimuB.png'" onmouseout="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/leimuA.png'" title=回到顶部></div><div class=sidebar_wo id=lamu><img src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/b2t/lamuA.png alt=雷姆 onmouseover="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/lamuB.png'" onmouseout="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/lamuA.png'" title=回到底部></div></div><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/lightgallery.js@1.2.0/dist/css/lightgallery.min.css><script type=text/javascript src=https://cdn.jsdelivr.net/npm/smooth-scroll@16.1.3/dist/smooth-scroll.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lazysizes@5.2.2/lazysizes.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lightgallery.js@1.2.0/dist/js/lightgallery.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lg-thumbnail.js@1.2.0/dist/lg-thumbnail.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lg-zoom.js@1.2.0/dist/lg-zoom.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/sharer.js@0.4.0/sharer.min.js></script><script type=text/javascript>window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":30},"comment":{},"lightGallery":{"actualSize":false,"exThumbImage":"data-thumbnail","hideBarsDelay":2000,"selector":".lightgallery","speed":400,"thumbContHeight":80,"thumbWidth":80,"thumbnail":true}};</script><script type=text/javascript src=/js/theme.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/jquery@2.1.3/dist/jquery.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js></script><script>var $cdnPrefix="https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master";</script><script type=text/javascript src=/js/custom.js></script><script>var _hmt=_hmt||[];(function(){var hm=document.createElement("script");hm.src="https://hm.baidu.com/hm.js?6f278ed0fdb01edb3b1e7398379e5432";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();</script></body></html>